package com.study.module.log;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author 王霄
 * @date 2020/9/22
 */
@Aspect
@Component
@Slf4j
public class LogAspect {


    @Pointcut("@annotation(com.study.module.log.LogParams)")
    public void logMethodPointcut() {
    }

    @Before("logMethodPointcut()")
    public void beforeLogMethod(JoinPoint joinPoint) {
        try {
            beforeLogMethod0(joinPoint);
        } catch (Exception e) {
            log.info(String.format("打印调用日志发生异常！pointcut=%s", joinPoint), e);
        }
    }

    @Before("logMethodPointcut()")
    public void beforeLogMethod0(JoinPoint joinPoint) {

        // 获取被代理对象的类
        Class<?> targetCls = joinPoint.getTarget().getClass();

        // 获取方法名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getName();

        // 获取排除项与toString项
        LogParams logParams = signature.getMethod().getAnnotation(LogParams.class);
        Set<Class<?>> excludes = Stream.of(logParams.excludes()).collect(Collectors.toSet());
        Set<Class<?>> toStrings = Stream.of(logParams.toStrings()).collect(Collectors.toSet());


        // 获取参数名与参数列表值，排除不需要打印的参数
        Object[] args = joinPoint.getArgs();
        String[] names = signature.getParameterNames();
        Class<?>[] types = signature.getParameterTypes();
        int len = names.length;
        Map<String, Object> params = new HashMap<>();
        for (int i = 0; i < len; i++) {
            // 跳过排除项
            if (excludes.contains(types[i])) {
                continue;
            }
            Object arg = args[i];
            // 使用toString方法打印toString项
            if (arg != null && toStrings.contains(types[i])) {
                arg = arg.toString();
            }
            params.put(names[i], arg);
        }

        // 打印log
        log.info("{}.{}().Req: {}", targetCls.getSimpleName(), methodName, JSONObject.toJSONString(params));
    }
}
